 /*******************************************************************************
  * Copyright (c) 2000, 2005 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.update.core;

 import java.net.URL ;
 import java.util.ArrayList ;
 import java.util.Arrays ;
 import java.util.HashSet ;
 import java.util.List ;
 import java.util.Set ;
 import java.util.Vector ;

 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.SubProgressMonitor;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.update.core.model.ContentEntryModel;
 import org.eclipse.update.core.model.FeatureModel;
 import org.eclipse.update.core.model.FeatureReferenceModel;
 import org.eclipse.update.core.model.ImportModel;
 import org.eclipse.update.core.model.InstallAbortedException;
 import org.eclipse.update.core.model.NonPluginEntryModel;
 import org.eclipse.update.core.model.PluginEntryModel;
 import org.eclipse.update.core.model.URLEntryModel;
 import org.eclipse.update.internal.core.ErrorRecoveryLog;
 import org.eclipse.update.internal.core.InstallHandlerProxy;
 import org.eclipse.update.internal.core.InstallRegistry;
 import org.eclipse.update.internal.core.Messages;
 import org.eclipse.update.internal.core.TargetFeature;
 import org.eclipse.update.internal.core.UpdateCore;
 import org.eclipse.update.internal.core.UpdateManagerUtils;
 import org.eclipse.update.internal.core.UpdateSiteIncludedFeatureReference;

 /**
  * Convenience implementation of a feature.
  * <p>
  * This class may be instantiated or subclassed by clients.
  * </p>
  * <p>
  * <b>Note:</b> This class/interface is part of an interim API that is still under development and expected to
  * change significantly before reaching stability. It is being made available at this early stage to solicit feedback
  * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
  * (repeatedly) as the API evolves.
  * </p>
  * @see org.eclipse.update.core.IFeature
  * @see org.eclipse.update.core.model.FeatureModel
  * @since 2.0
  */
 public class Feature extends FeatureModel implements IFeature {

     /**
      * Simple file name of the default feature manifest file
      * @since 2.0
      */
     public static final String FEATURE_FILE = "feature"; //$NON-NLS-1$

     /**
      * File extension of the default feature manifest file
      * @since 2.0
      */
     public static final String FEATURE_XML = FEATURE_FILE + ".xml"; //$NON-NLS-1$

     private ISite site; // feature site
 private IFeatureContentProvider featureContentProvider; // content provider
 private List /*of IFeatureReference*/
     includedFeatureReferences;

     //PERF: new instance variable
 private VersionedIdentifier versionId;

     private InstallAbortedException abortedException = null;

     /**
      * Feature default constructor
      *
      * @since 2.0
      */
     public Feature() {
     }

     /**
      * Compares two features for equality
      *
      * @param object feature object to compare with
      * @return <code>true</code> if the two features are equal,
      * <code>false</code> otherwise
      * @since 2.0
      */
     public boolean equals(Object object) {
         if (!(object instanceof IFeature))
             return false;
         IFeature f = (IFeature) object;
         return getVersionedIdentifier().equals(f.getVersionedIdentifier());
     }

     /**
      * Returns the feature identifier.
      *
      * @see IFeature#getVersionedIdentifier()
      * @since 2.0
      */
     public VersionedIdentifier getVersionedIdentifier() {
         if (versionId != null)
             return versionId;

         String id = getFeatureIdentifier();
         String ver = getFeatureVersion();
         if (id != null && ver != null) {
             try {
                 versionId = new VersionedIdentifier(id, ver);
                 return versionId;
             } catch (Exception e) {
                 UpdateCore.warn(
                     "Unable to create versioned identifier:" + id + ":" + ver); //$NON-NLS-1$ //$NON-NLS-2$
 }
         }

         versionId = new VersionedIdentifier(getURL().toExternalForm(), null);
         return versionId;
     }

     /**
      * Returns the site this feature is associated with.
      *
      * @see IFeature#getSite()
      * @since 2.0
      */
     public ISite getSite() {
         return site;
     }

     /**
      * Returns the feature URL.
      *
      * @see IFeature#getURL()
      * @since 2.0
      */
     public URL getURL() {
         IFeatureContentProvider contentProvider = null;
         try {
             contentProvider = getFeatureContentProvider();
         } catch (CoreException e) {
             UpdateCore.warn("No content Provider", e); //$NON-NLS-1$
 }
         return (contentProvider != null) ? contentProvider.getURL() : null;
     }

     /**
      * Returns an information entry referencing the location of the
      * feature update site.
      *
      * @see IFeature#getUpdateSiteEntry()
      * @since 2.0
      */
     public IURLEntry getUpdateSiteEntry() {
         return (IURLEntry) getUpdateSiteEntryModel();
     }

     /**
      * Return an array of information entries referencing locations of other
      * update sites.
      *
      * @see IFeature#getDiscoverySiteEntries()
      * @since 2.0
      */
     public IURLEntry[] getDiscoverySiteEntries() {
         URLEntryModel[] result = getDiscoverySiteEntryModels();
         if (result.length == 0)
             return new IURLEntry[0];
         else
             return (IURLEntry[]) result;
     }

     /**
      * Returns and optional custom install handler entry.
      *
      * @see IFeature#getInstallHandlerEntry()
      * @since 2.0
      */
     public IInstallHandlerEntry getInstallHandlerEntry() {
         return (IInstallHandlerEntry) getInstallHandlerModel();
     }

     /**
      * Returns the feature description.
      *
      * @see IFeature#getDescription()
      * @since 2.0
      */
     public IURLEntry getDescription() {
         return (IURLEntry) getDescriptionModel();
     }

     /**
      * Returns the copyright information for the feature.
      *
      * @see IFeature#getCopyright()
      * @since 2.0
      */
     public IURLEntry getCopyright() {
         return (IURLEntry) getCopyrightModel();
     }

     /**
      * Returns the license information for the feature.
      *
      * @see IFeature#getLicense()
      * @since 2.0
      */
     public IURLEntry getLicense() {
         return (IURLEntry) getLicenseModel();
     }

     /**
      * Return optional image for the feature.
      *
      * @see IFeature#getImage()
      * @since 2.0
      */
     public URL getImage() {
         return getImageURL();
     }

     /**
      * Return a list of plug-in dependencies for this feature.
      *
      * @see IFeature#getRawImports()
      * @since 2.0
      */
     public IImport[] getRawImports() {
         ImportModel[] result = getImportModels();
         if (result.length == 0)
             return new IImport[0];
         else
             return (IImport[]) result;
     }

     /**
      * Install the contents of this feature into the specified target feature.
      * This method is a reference implementation of the feature installation
      * protocol. Other concrete feature implementation that override this
      * method need to implement this protocol.
      *
      * @see IFeature#install(IFeature, IVerificationListener, IProgressMonitor)
      * @since 2.0
      */
     public IFeatureReference install(
         IFeature targetFeature,
         IVerificationListener verificationListener,
         IProgressMonitor progress)
         throws InstallAbortedException, CoreException {
         // call other API with all optional features, or setup variable meaning install all
 return install(targetFeature, null, verificationListener, progress);
     }

     /**
      * Install the contents of this feature into the specified target feature.
      * This method is a reference implementation of the feature installation
      * protocol. Other concrete feature implementation that override this
      * method need to implement this protocol.
      *
      * @see IFeature#install(IFeature, IVerificationListener, IProgressMonitor)
      * @since 2.0
      */
     public IFeatureReference install(
         IFeature targetFeature,
         IFeatureReference[] optionalfeatures,
         IVerificationListener verificationListener,
         IProgressMonitor progress)
         throws InstallAbortedException, CoreException {

         //DEBUG
 debug("Installing...:" + getURL().toExternalForm()); //$NON-NLS-1$
 ErrorRecoveryLog recoveryLog = ErrorRecoveryLog.getLog();

         // make sure we have an InstallMonitor
 InstallMonitor monitor;
         if (progress == null)
             monitor = new InstallMonitor(new NullProgressMonitor());
         else if (progress instanceof InstallMonitor)
             monitor = (InstallMonitor) progress;
         else
             monitor = new InstallMonitor(progress);

         // Setup optional install handler
 InstallHandlerProxy handler =
             new InstallHandlerProxy(
                 IInstallHandler.HANDLER_ACTION_INSTALL,
                 this,
                 this.getInstallHandlerEntry(),
                 monitor);
         boolean success = false;
         Throwable originalException = null;
         abortedException = null;

         // Get source feature provider and verifier.
 // Initialize target variables.
 IFeatureContentProvider provider = getFeatureContentProvider();
         IVerifier verifier = provider.getVerifier();
         IFeatureReference result = null;
         IFeatureReference alreadyInstalledFeature = null;
         IFeatureContentConsumer consumer = null;
         IPluginEntry[] targetSitePluginEntries = null;
         ArrayList justInstalledPlugins = new ArrayList ();

         try {
             // determine list of plugins to install
 // find the intersection between the plugin entries already contained
 // on the target site, and plugin entries packaged in source feature
 IPluginEntry[] sourceFeaturePluginEntries = getPluginEntries();
             ISite targetSite = targetFeature.getSite();
             if (targetSite == null) {
                 debug("The site to install in is null"); //$NON-NLS-1$
 targetSitePluginEntries = new IPluginEntry[0];
             } else {
                 targetSitePluginEntries = targetSite.getPluginEntries();
             }
             IPluginEntry[] pluginsToInstall =
                 UpdateManagerUtils.diff(
                     sourceFeaturePluginEntries,
                     targetSitePluginEntries);
             INonPluginEntry[] nonPluginsToInstall = getNonPluginEntries();

             IFeatureReference[] children = getIncludedFeatureReferences();
             if (optionalfeatures != null) {
                 children =
                     UpdateManagerUtils.optionalChildrenToInstall(
                         children,
                         optionalfeatures);
             }

             // determine number of monitor tasks
 // 2 tasks for the feature jar (download/verify + install)
 // + 2*n tasks for plugin entries (download/verify + install for each)
 // + 1*m tasks per non-plugin data entry (download for each)
 // + 1 task for custom non-plugin entry handling (1 for all combined)
 // + 5*x tasks for children features (5 subtasks per install)
 int taskCount =
                 2
                     + 2 * pluginsToInstall.length
                     + nonPluginsToInstall.length
                     + 1
                     + 5 * children.length;
             monitor.beginTask("", taskCount); //$NON-NLS-1$
 SubProgressMonitor subMonitor = null;

             // start log
 recoveryLog.open(ErrorRecoveryLog.START_INSTALL_LOG);

             // Start the installation tasks
 handler.installInitiated();

             // Download and verify feature archive(s)
 ContentReference[] references =
                 provider.getFeatureEntryArchiveReferences(monitor);
             verifyReferences(
                 verifier,
                 references,
                 monitor,
                 verificationListener,
                 true);
             monitorWork(monitor, 1);
             
             // Download and verify plugin archives
 for (int i = 0; i < pluginsToInstall.length; i++) {
                 references = provider.getPluginEntryArchiveReferences(
                         pluginsToInstall[i], monitor);
                 verifyReferences(verifier, references, monitor,
                                 verificationListener, false);
                 monitorWork(monitor, 1);
             }
             
             handler.pluginsDownloaded(pluginsToInstall);

             Vector filteredPlugins = new Vector ();
             // Download non-plugin archives. Verification handled by optional
 // install handler
 for (int i = 0; i < nonPluginsToInstall.length; i++) {
                 if (handler.acceptNonPluginData(nonPluginsToInstall[i])) {
                     references = provider.getNonPluginEntryArchiveReferences(
                             nonPluginsToInstall[i], monitor);
                     monitorWork(monitor, 1);
                     filteredPlugins.add(nonPluginsToInstall[i]);
                 }
             }
             nonPluginsToInstall = (INonPluginEntry[]) filteredPlugins
                     .toArray(new INonPluginEntry[0]);
             handler.nonPluginDataDownloaded(nonPluginsToInstall,
                     verificationListener);

             // All archives are downloaded and verified. Get ready to install
 consumer = targetFeature.getFeatureContentConsumer();

             // install the children feature
 // check if they are optional, and if they should be installed [2.0.1]
 for (int i = 0; i < children.length; i++) {
                 IFeature childFeature = null;
                 try {
                     childFeature = children[i].getFeature(null);
                 } catch (CoreException e) {
                     UpdateCore.warn(null, e);
                 }
                 if (childFeature != null) {
                     subMonitor = new SubProgressMonitor(monitor, 5);
                     ((Site) targetSite).install(// need to cast
 childFeature,
                         optionalfeatures,
                         consumer,
                         verifier,
                         verificationListener,
                         subMonitor);
                 }
             }

             // Install plugin files
 for (int i = 0; i < pluginsToInstall.length; i++) {
                 // if another feature has already installed this plugin, skip it
 if (InstallRegistry.getInstance().isPluginJustInstalled(pluginsToInstall[i])) {
                     monitor.worked(1);
                     continue;
                 }
                 IContentConsumer pluginConsumer =
                     consumer.open(pluginsToInstall[i]);
                 // TODO consumer.open returns either
 // SiteFilePackedPluginContentConsumer or SiteFilePluginContentConsumer
 // and they are fed either
 // PluginEntryArchiveReference or PluginEntryContentReferences
 // it would be better to have one that is given PluginEntryArchiveReference
 // but it would break external IContentConsumers

                 if(pluginsToInstall[i] instanceof PluginEntryModel && !((PluginEntryModel)pluginsToInstall[i]).isUnpack()){
                     // plugin can run from a jar
 references = provider.getPluginEntryArchiveReferences(
                             pluginsToInstall[i], monitor);
                 } else{
                     // plugin must be unpacked
 references =
                         provider.getPluginEntryContentReferences(
                             pluginsToInstall[i],
                             monitor);
                 }

                 String msg = ""; //$NON-NLS-1$
 subMonitor = new SubProgressMonitor(monitor, 1);
                 VersionedIdentifier pluginVerId =
                     pluginsToInstall[i].getVersionedIdentifier();
                 String pluginID =
                     (pluginVerId == null) ? "" : pluginVerId.getIdentifier(); //$NON-NLS-1$
 msg = NLS.bind(Messages.Feature_TaskInstallPluginFiles, (new String [] { pluginID }));

                 for (int j = 0; j < references.length; j++) {
                     setMonitorTaskName(
                         subMonitor,
                         msg + references[j].getIdentifier());
                     pluginConsumer.store(references[j], subMonitor);
                 }

                 if (monitor.isCanceled())
                     abort();
                 else {
                     justInstalledPlugins.add(pluginsToInstall[i]);
                     InstallRegistry.registerPlugin(pluginsToInstall[i]);
                 }
             }

             // check if we need to install feature files [16718]
 // store will throw CoreException if another feature is already
 // installed in the same place
 alreadyInstalledFeature = featureAlreadyInstalled(targetSite);
             // 18867
 if (alreadyInstalledFeature == null) {
                 //Install feature files
 references = provider.getFeatureEntryContentReferences(monitor);

                 String msg = ""; //$NON-NLS-1$
 subMonitor = new SubProgressMonitor(monitor, 1);
                 msg = Messages.Feature_TaskInstallFeatureFiles;

                 for (int i = 0; i < references.length; i++) {
                     setMonitorTaskName(
                         subMonitor,
                         msg + " " + references[i].getIdentifier()); //$NON-NLS-1$
 consumer.store(references[i], subMonitor);
                 }

                 if (monitor.isCanceled())
                     abort();
                 else
                     InstallRegistry.registerFeature(this);
             } else {
                 if (monitor.isCanceled())
                     abort();
                 else
                     monitor.worked(1);
             }

             // call handler to complete installation (eg. handle non-plugin entries)
 handler.completeInstall(consumer);
             monitorWork(monitor, 1);

             // indicate install success
 success = true;

         } catch (InstallAbortedException e) {
             abortedException = e;
         } catch (CoreException e) {
             originalException = e;
         } finally {
             Exception newException = null;
             try {
                 if (consumer != null) {
                     if (success) {
                         result = consumer.close();
                         if (result == null) {
                             result = alreadyInstalledFeature; // 18867
 if (result != null
                                 && optionalfeatures != null
                                 && optionalfeatures.length > 0) {
                                 // reinitialize as new optional children may have been installed
 reinitializeFeature(result);
                             }
                         }
                         // close the log
 recoveryLog.close(ErrorRecoveryLog.END_INSTALL_LOG);
                     } else {
                         // unregister the just installed plugins
 for (int i=0; i<justInstalledPlugins.size(); i++)
                             InstallRegistry.unregisterPlugin(((IPluginEntry)justInstalledPlugins.get(i)));
                         consumer.abort();
                     }
                 }
                 handler.installCompleted(success);
                 // if abort is done, no need for the log to stay
 recoveryLog.delete();
             } catch (CoreException e) {
                 newException = e;
             }

             // original exception wins unless it is InstallAbortedException
 // and an error occured during abort
 if (originalException != null) {
                 throw Utilities.newCoreException(
                     NLS.bind(Messages.InstallHandler_error, (new String [] { this.getLabel() })),
                     originalException);
             }

             if (newException != null)
                 throw Utilities.newCoreException(
                     NLS.bind(Messages.InstallHandler_error, (new String [] { this.getLabel() })),
                     newException);

             if (abortedException != null) {
                 throw abortedException;
             }

         }
         return result;
     }

     /**
      * Returns an array of plug-in entries referenced by this feature
      *
      * @see IFeature#getPluginEntries()
      * @since 2.0
      */
     public IPluginEntry[] getRawPluginEntries() {
         PluginEntryModel[] result = getPluginEntryModels();
         if (result.length == 0)
             return new IPluginEntry[0];
         else
             return (IPluginEntry[]) result;
     }

     /*
      * Method filter.
      * @param result
      * @return IPluginEntry[]
      */
     private IPluginEntry[] filterPluginEntry(IPluginEntry[] all) {
         List list = new ArrayList ();
         if (all != null) {
             for (int i = 0; i < all.length; i++) {
                 if (UpdateManagerUtils.isValidEnvironment(all[i]))
                     list.add(all[i]);
             }
         }

         IPluginEntry[] result = new IPluginEntry[list.size()];
         if (!list.isEmpty()) {
             list.toArray(result);
         }

         return result;
     }

     /**
      * Returns the count of referenced plug-in entries.
      *
      * @see IFeature#getPluginEntryCount()
      * @since 2.0
      */
     public int getPluginEntryCount() {
         return getPluginEntries().length;
     }

     /**
      * Returns an array of non-plug-in entries referenced by this feature
      *
      * @see IFeature#getNonPluginEntries()
      * @since 2.0
      */
     public INonPluginEntry[] getRawNonPluginEntries() {
         NonPluginEntryModel[] result = getNonPluginEntryModels();
         if (result.length == 0)
             return new INonPluginEntry[0];
         else
             return (INonPluginEntry[]) result;
     }

     /**
      * Returns the count of referenced non-plug-in entries.
      *
      * @see IFeature#getNonPluginEntryCount()
      * @since 2.0
      */
     public int getNonPluginEntryCount() {
         return getNonPluginEntryModels().length;
     }

     /**
      * Returns an array of feature references included by this feature
      *
      * @return an erray of feature references, or an empty array.
      * @since 2.0
      */
     public IIncludedFeatureReference[] getRawIncludedFeatureReferences()
         throws CoreException {
         if (includedFeatureReferences == null)
             initializeIncludedReferences();

         if (includedFeatureReferences.size() == 0)
             return new IncludedFeatureReference[0];

         return (IIncludedFeatureReference[]) includedFeatureReferences.toArray(
             new IIncludedFeatureReference[includedFeatureReferences.size()]);
     }
     /**
      * Returns the download size of the feature, if it can be determined.
      *
      * @see IFeature#getDownloadSize()
      * @since 2.0
      */
     public long getDownloadSize() {
         try {
             Set allPluginEntries = new HashSet ();
             Set allNonPluginEntries = new HashSet ();

             IPluginEntry[] plugins = getPluginEntries();
             allPluginEntries.addAll(Arrays.asList(plugins));
             INonPluginEntry[] nonPlugins = getNonPluginEntries();
             allNonPluginEntries.addAll(Arrays.asList(nonPlugins));

             IFeatureReference[] children = getIncludedFeatureReferences();
             for (int i = 0; i < children.length; i++) {
                 plugins = children[i].getFeature(null).getPluginEntries();
                 allPluginEntries.addAll(Arrays.asList(plugins));
                 nonPlugins = children[i].getFeature(null).getNonPluginEntries();
                 allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
             }

             IPluginEntry[] totalPlugins =
                 new IPluginEntry[allPluginEntries.size()];
             INonPluginEntry[] totalNonPlugins =
                 new INonPluginEntry[allNonPluginEntries.size()];
             if (allPluginEntries.size() != 0) {
                 allPluginEntries.toArray(totalPlugins);
             }
             if (allNonPluginEntries.size() != 0) {
                 allNonPluginEntries.toArray(totalNonPlugins);
             }

             return getFeatureContentProvider().getDownloadSizeFor(
                 totalPlugins,
                 totalNonPlugins);

         } catch (CoreException e) {
             UpdateCore.warn(null, e);
             return ContentEntryModel.UNKNOWN_SIZE;
         }
     }

     /**
      * Returns the install size of the feature, if it can be determined.
      *
      * @see IFeature#getInstallSize()
      * @since 2.0
      */
     public long getInstallSize() {
         try {
             Set allPluginEntries = new HashSet ();
             Set allNonPluginEntries = new HashSet ();

             IPluginEntry[] plugins = getPluginEntries();
             allPluginEntries.addAll(Arrays.asList(plugins));
             INonPluginEntry[] nonPlugins = getNonPluginEntries();
             allNonPluginEntries.addAll(Arrays.asList(nonPlugins));

             IFeatureReference[] children = getIncludedFeatureReferences();
             for (int i = 0; i < children.length; i++) {
                 plugins = children[i].getFeature(null).getPluginEntries();
                 allPluginEntries.addAll(Arrays.asList(plugins));
                 nonPlugins = children[i].getFeature(null).getNonPluginEntries();
                 allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
             }

             IPluginEntry[] totalPlugins =
                 new IPluginEntry[allPluginEntries.size()];
             INonPluginEntry[] totalNonPlugins =
                 new INonPluginEntry[allNonPluginEntries.size()];
             if (allPluginEntries.size() != 0) {
                 allPluginEntries.toArray(totalPlugins);
             }
             if (allNonPluginEntries.size() != 0) {
                 allNonPluginEntries.toArray(totalNonPlugins);
             }

             return getFeatureContentProvider().getInstallSizeFor(
                 totalPlugins,
                 totalNonPlugins);

         } catch (CoreException e) {
             UpdateCore.warn(null, e);
             return ContentEntryModel.UNKNOWN_SIZE;
         }
     }

     /**
      * Returns the content provider for this feature.
      *
      * @see IFeature#getFeatureContentProvider()
      * @since 2.0
      */
     public IFeatureContentProvider getFeatureContentProvider()
         throws CoreException {
         if (featureContentProvider == null) {
             throw Utilities.newCoreException(
                 NLS.bind(Messages.Feature_NoContentProvider, (new String [] { getVersionedIdentifier().toString() })),
                 null);
         }
         return this.featureContentProvider;
     }

     /**
      * Returns the content consumer for this feature.
      *
      * @see IFeature#getFeatureContentConsumer()
      * @since 2.0
      */
     public IFeatureContentConsumer getFeatureContentConsumer()
         throws CoreException {
         throw new UnsupportedOperationException ();
     }

     /**
      * Sets the site for this feature.
      *
      * @see IFeature#setSite(ISite)
      * @since 2.0
      */
     public void setSite(ISite site) throws CoreException {
         if (this.site != null) {
             String featureURLString =
                 (getURL() != null) ? getURL().toExternalForm() : ""; //$NON-NLS-1$
 throw Utilities.newCoreException(
                 NLS.bind(Messages.Feature_SiteAlreadySet, (new String [] { featureURLString })),
                 null);
         }
         this.site = site;
     }

     /**
      * Sets the content provider for this feature.
      *
      * @see IFeature#setFeatureContentProvider(IFeatureContentProvider)
      * @since 2.0
      */
     public void setFeatureContentProvider(IFeatureContentProvider featureContentProvider) {
         this.featureContentProvider = featureContentProvider;
         featureContentProvider.setFeature(this);
     }

     /**
      * Return the string representation of this fetaure
      *
      * @return feature as string
      * @since 2.0
      */
     public String toString() {
         String URLString =
             (getURL() == null)
                 ? Messages.Feature_NoURL
                 : getURL().toExternalForm();

         String verString =
             NLS.bind(Messages.Feature_FeatureVersionToString, (new String [] { URLString, getVersionedIdentifier().toString() }));
         String label = getLabel() == null ? "" : getLabel(); //$NON-NLS-1$
 return verString + " [" + label + "]"; //$NON-NLS-1$ //$NON-NLS-2$
 }

     /*
      * Installation has been cancelled, abort and revert
      */
     private void abort() throws CoreException {
         String msg = Messages.Feature_InstallationCancelled;
         throw new InstallAbortedException(msg, null);
     }

     /*
      * Initializes includes feature references
      * If the included feature reference is found on the site, add it to the List
      * Otherwise attempt to instanciate it using the same type as this feature and
      * using the default location on the site.
      */
     private void initializeIncludedReferences() throws CoreException {
         includedFeatureReferences = new ArrayList ();

         IIncludedFeatureReference[] nestedFeatures = getFeatureIncluded();
         if (nestedFeatures.length == 0)
             return;

         ISite site = getSite();
         if (site == null)
             return;

         for (int i = 0; i < nestedFeatures.length; i++) {
             IIncludedFeatureReference include = nestedFeatures[i];
             IIncludedFeatureReference newRef =
                 getPerfectIncludeFeature(site, include);
             includedFeatureReferences.add(newRef);
         }
     }

     /*
      *
      */
     private IIncludedFeatureReference getPerfectIncludeFeature(
         ISite site,
         IIncludedFeatureReference include)
         throws CoreException {

         // [20367] no site, cannot initialize nested references
 ISiteFeatureReference[] refs = site.getFeatureReferences();
         VersionedIdentifier identifier = include.getVersionedIdentifier();

         // too long to compute if not a file system
 // other solution would be to parse feature.xml
 // when parsing file system to create archive features/FeatureId_Ver.jar
 if ("file".equals(site.getURL().getProtocol())) { //$NON-NLS-1$
 // check if declared on the Site
 if (refs != null) {
                 for (int ref = 0; ref < refs.length; ref++) {
                     if (refs[ref] != null) {
                         VersionedIdentifier id =
                             refs[ref].getVersionedIdentifier();
                         if (identifier.equals(id)) {
                             // found a ISiteFeatureReference that matches our IIncludedFeatureReference
 IncludedFeatureReference newRef =
                                 new IncludedFeatureReference(refs[ref]);
                             newRef.isOptional(include.isOptional());
                             if (include instanceof FeatureReferenceModel)
                                 newRef.setLabel(
                                     ((FeatureReferenceModel) include)
                                         .getLabel());
                             newRef.setSearchLocation(
                                 include.getSearchLocation());
                             return newRef;
                         }
                     }
                 }
             }
         }

         // instantiate by mapping it based on the site.xml
 // in future we may ask for a factory to create the feature ref
 IncludedFeatureReference newRef = new UpdateSiteIncludedFeatureReference(include);
         newRef.setSite(getSite());
         IFeatureReference parentRef = getSite().getFeatureReference(this);
         if (parentRef instanceof FeatureReference) {
          newRef.setType(((FeatureReference) parentRef).getType());
         }
         String featureID = Site.DEFAULT_FEATURE_PATH + identifier.toString();
         if(this instanceof TargetFeature)
          featureID = featureID + "/"; //$NON-NLS-1$
 else
          featureID = featureID + ".jar"; //$NON-NLS-1$
 URL featureURL =
             getSite().getSiteContentProvider().getArchiveReference(featureID);
         newRef.setURL(featureURL);
         newRef.setFeatureIdentifier(identifier.getIdentifier());
         newRef.setFeatureVersion(identifier.getVersion().toString());
         try {
             newRef.resolve(getSite().getURL(), null);
             // no need to get the bundle
 return newRef;
         } catch (Exception e) {
             throw Utilities.newCoreException(
                 NLS.bind(Messages.Feature_UnableToInitializeFeatureReference, (new String [] { identifier.toString() })),
                 e);
         }
     }

     /*
      *
      */
     private void debug(String trace) {
         //DEBUG
 if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL) {
             UpdateCore.debug(trace);
         }
     }

     /*
      *
      */
     private void setMonitorTaskName(
         IProgressMonitor monitor,
         String taskName) {
         if (monitor != null)
             monitor.setTaskName(taskName);
     }

     /*
      *
      */
     private void monitorWork(IProgressMonitor monitor, int tick)
         throws CoreException {
         if (monitor != null) {
             monitor.worked(tick);
             if (monitor.isCanceled()) {
                 abort();
             }
         }
     }

     /*
      *
      */
     private void verifyReferences(
         IVerifier verifier,
         ContentReference[] references,
         InstallMonitor monitor,
         IVerificationListener verificationListener,
         boolean isFeature)
         throws CoreException {
         IVerificationResult vr = null;
         if (verifier != null) {
             for (int j = 0; j < references.length; j++) {
                 vr = verifier.verify(this, references[j], isFeature, monitor);
                 if (vr != null) {
                     if (verificationListener == null)
                         return;

                     int result = verificationListener.prompt(vr);

                     if (result == IVerificationListener.CHOICE_ABORT) {
                         String msg = Messages.JarVerificationService_CancelInstall;
                         Exception e = vr.getVerificationException();
                         throw new InstallAbortedException(msg, e);
                     }
                     if (result == IVerificationListener.CHOICE_ERROR) {
                         throw Utilities
                             .newCoreException(
                                 Messages.JarVerificationService_UnsucessfulVerification,
                         vr.getVerificationException());
                     }
                 }
             }
         }
     }

     /*
      * returns reference if the same feature is installed on the site
      * [18867]
      */
     private IFeatureReference featureAlreadyInstalled(ISite targetSite) {

         ISiteFeatureReference[] references = targetSite.getFeatureReferences();
         IFeatureReference currentReference = null;
         for (int i = 0; i < references.length; i++) {
             currentReference = references[i];
            // do not compare URL
 try {
                if (this.equals(currentReference.getFeature(null)))
                    return currentReference; // 18867
 } catch (CoreException e) {
                UpdateCore.warn(null, e);
            }
        }

        UpdateCore.warn(
            "ValidateAlreadyInstalled:Feature " //$NON-NLS-1$
 + this
                + " not found on site:" //$NON-NLS-1$
 + this.getURL());
        return null;
    }

    /*
     * re initialize children of the feature, invalidate the cache
     * @param result FeatureReference to reinitialize.
     */
    private void reinitializeFeature(IFeatureReference referenceToReinitialize) {

        if (referenceToReinitialize == null)
            return;

        if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION)
            UpdateCore.debug(
                "Re initialize feature reference:" + referenceToReinitialize); //$NON-NLS-1$

        IFeature feature = null;
        try {
            feature = referenceToReinitialize.getFeature(null);
            if (feature != null && feature instanceof Feature) {
                ((Feature) feature).initializeIncludedReferences();
            }
            // bug 24981 - recursively go into hierarchy
 // only if site if file
 ISite site = referenceToReinitialize.getSite();
            if (site == null)
                return;
            URL url = site.getURL();
            if (url == null)
                return;
            if ("file".equals(url.getProtocol())) { //$NON-NLS-1$
 IFeatureReference[] included =
                    feature.getIncludedFeatureReferences();
                for (int i = 0; i < included.length; i++) {
                    reinitializeFeature(included[i]);
                }
            }
        } catch (CoreException e) {
            UpdateCore.warn("", e); //$NON-NLS-1$
 }
    }

    /**
     * @see org.eclipse.update.core.IFeature#getRawIncludedFeatureReferences()
     */
    public IIncludedFeatureReference[] getIncludedFeatureReferences()
        throws CoreException {
        return filterFeatures(getRawIncludedFeatureReferences());
    }

    /*
     * Method filterFeatures.
     * Also implemented in Site
     *
     * @param list
     * @return List
     */
    private IIncludedFeatureReference[] filterFeatures(IIncludedFeatureReference[] allIncluded) {
        List list = new ArrayList ();
        if (allIncluded != null) {
            for (int i = 0; i < allIncluded.length; i++) {
                IIncludedFeatureReference included = allIncluded[i];
                if (UpdateManagerUtils.isValidEnvironment(included))
                    list.add(included);
                else {
                    if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS) {
                        UpdateCore.warn(
                            "Filtered out feature reference:" + included); //$NON-NLS-1$
 }
                }
            }
        }

        IIncludedFeatureReference[] result =
            new IIncludedFeatureReference[list.size()];
        if (!list.isEmpty()) {
            list.toArray(result);
        }

        return result;
    }

    /**
     * @see org.eclipse.update.core.IFeature#getRawNonPluginEntries()
     */
    public INonPluginEntry[] getNonPluginEntries() {
        return filterNonPluginEntry(getRawNonPluginEntries());
    }

    /**
     * Method filterPluginEntry.
     * @param all
     * @return INonPluginEntry[]
     */
    private INonPluginEntry[] filterNonPluginEntry(INonPluginEntry[] all) {
        List list = new ArrayList ();
        if (all != null) {
            for (int i = 0; i < all.length; i++) {
                if (UpdateManagerUtils.isValidEnvironment(all[i]))
                    list.add(all[i]);
            }
        }

        INonPluginEntry[] result = new INonPluginEntry[list.size()];
        if (!list.isEmpty()) {
            list.toArray(result);
        }

        return result;
    }

    /**
     * @see org.eclipse.update.core.IFeature#getRawPluginEntries()
     */
    public IPluginEntry[] getPluginEntries() {
        return filterPluginEntry(getRawPluginEntries());
    }

    /**
     * @see org.eclipse.update.core.IFeature#getImports()
     */
    public IImport[] getImports() {
        return filterImports(getRawImports());
    }

    /**
     * Method filterImports.
     * @param all
     * @return IImport[]
     */
    private IImport[] filterImports(IImport[] all) {
        List list = new ArrayList ();
        if (all != null) {
            for (int i = 0; i < all.length; i++) {
                if (UpdateManagerUtils.isValidEnvironment(all[i]))
                    list.add(all[i]);
            }
        }

        IImport[] result = new IImport[list.size()];
        if (!list.isEmpty()) {
            list.toArray(result);
        }

        return result;
    }

}
